﻿namespace estoque
{
    using System;
    using System.Collections.Generic;
    using System.DirectoryServices;
    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.WorkItemTracking.Client;

    public enum WorkItemTypes
    {
        Bug,
        Task
    }

    public class TeamFoundationHandler
    {
        private WorkItemStore store;
        private WorkItemTypeCollection workItemTypeCollection;

        private string projectName;

        /// <summary>
        /// Initializes a new instance of the <see cref="TeamFoundationHandler"/> class.
        /// </summary>
        /// <param name="serverName">Name of the server.</param>
        /// <param name="projectName">Name of the project.</param>
        public TeamFoundationHandler(string serverName, string projectName)
        {
            Uri serverUri = new Uri(serverName);
            TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(serverUri);
            this.store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));

            this.projectName = projectName;
        }

        /// <summary>
        /// Gets the work item type collection.
        /// </summary>
        /// <value>The work item type collection.</value>
        private WorkItemTypeCollection WorkItemTypeCollection
        {
            get
            {
                if (this.workItemTypeCollection == null)
                {
                    this.workItemTypeCollection = this.store.Projects[this.projectName].WorkItemTypes;
                }

                return this.workItemTypeCollection;
            }
        }

        /// <summary>
        /// Gets all work items.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <returns>
        /// a <c>WorkItemCollection</c> with all workitems of the give user
        /// </returns>
        public WorkItemCollection GetAllWorkItems(string userName)
        {
            // Perform WIQL Query
            // http://msdn.microsoft.com/en-us/library/bb130198(v=VS.80).aspx
            WorkItemCollection wic = this.store.Query(
               " SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
               " FROM WorkItems " +
               " WHERE [System.TeamProject] = '" + this.projectName + "'" +
               " AND [System.AssignedTo] = '" + userName + "'" +
               " ORDER BY [System.WorkItemType], [System.Id]");

            return wic;
        }

        /// <summary>
        /// Gets all work items.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <returns>
        /// a <c>WorkItemCollection</c> with all workitems of the give user
        /// </returns>
        public WorkItemCollection GetAllOpenBugs()
        {
            string displayName = this.GetActualUserName();

            // Perform WIQL Query
            // http://msdn.microsoft.com/en-us/library/bb130198(v=VS.80).aspx
            WorkItemCollection wic = this.store.Query(
               " SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
               " FROM WorkItems " +
               " WHERE [System.TeamProject] = '" + this.projectName + "'" +
               " AND [System.AssignedTo] = '" + displayName + "'" +
               " AND [System.State] != 'Closed'" +
               " AND [System.WorkItemType] = 'Bug'" +
               " ORDER BY [System.Title], [System.Id]");

            return wic;
        }

        public WorkItemCollection GetAllOpenWorkItems()
        {
            string displayName = this.GetActualUserName();

            // Perform WIQL Query
            // http://msdn.microsoft.com/en-us/library/bb130198(v=VS.80).aspx
            WorkItemCollection wic = this.store.Query(
               " SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
               " FROM WorkItems " +
               " WHERE [System.TeamProject] = '" + this.projectName + "'" +
               " AND [System.AssignedTo] = '" + displayName + "'" +
               " AND [System.State] != 'Closed'" +
               " ORDER BY [System.Title], [System.Id]");

            return wic;
        }

        /// <summary>
        /// Gets all work items.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <returns>
        /// a <c>WorkItemCollection</c> with all workitems of the give user
        /// </returns>
        public WorkItemCollection GetAllBugs()
        {
            string displayName = this.GetActualUserName();

            // Perform WIQL Query
            // http://msdn.microsoft.com/en-us/library/bb130198(v=VS.80).aspx
            WorkItemCollection wic = this.store.Query(
               " SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
               " FROM WorkItems " +
               " WHERE [System.TeamProject] = '" + this.projectName + "'" +
               " AND [System.AssignedTo] = '" + displayName + "'" +
               " AND [System.WorkItemType] = 'Bug'" +
               " ORDER BY [System.Title], [System.Id]");

            return wic;
        }

        /// <summary>
        /// Gets all area nodes.
        /// </summary>
        /// <returns>a List with <c>Node</c>s</returns>
        public List<Node> GetAllAreaNodes()
        {
            NodeCollection nc = this.store.Projects[this.projectName].AreaRootNodes;

            List<Node> nodes = new List<Node>();
            nodes.AddRange(MakeFlatList(nc));

            return nodes;
        }

        /// <summary>
        /// Gets all iteration nodes.
        /// </summary>
        /// <returns>a List with <c>Node</c>s</returns>
        public List<Node> GetAllIterationNodes()
        {
            NodeCollection nc = this.store.Projects[this.projectName].IterationRootNodes;

            List<Node> nodes = new List<Node>();
            nodes.AddRange(MakeFlatList(nc));

            return nodes;
        }

        /// <summary>
        /// Gets all scenarios.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <returns></returns>
        public WorkItemCollection GetAllScenarios()
        {
            string displayName = this.GetActualUserName();

            // Perform WIQL Query
            // http://msdn.microsoft.com/en-us/library/bb130198(v=VS.80).aspx
            WorkItemCollection wic = this.store.Query(
               " SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
               " FROM WorkItems " +
               " WHERE [System.TeamProject] = '" + this.projectName + "'" +
               " AND [System.AssignedTo] = '" + displayName + "'" +
               " AND [System.State] != 'Closed'" +
               " AND [System.WorkItemType] = 'Scenario'" +
               " ORDER BY [System.Id]");

            return wic;
        }

        /// <summary>
        /// Adds the hyperlink.
        /// </summary>
        /// <param name="wi">The wi.</param>
        /// <param name="url">The URL (must be valid).</param>
        /// <param name="comment">The comment.</param>
        public static void AddHyperlink(WorkItem wi, string url, string comment = "")
        {
            // Link must be valid!
            Hyperlink link = new Hyperlink(url);
            link.Comment = comment;
            wi.Links.Add(link);
        }

        /// <summary>
        /// Adds the work item link.
        /// </summary>
        /// <param name="wi">The wi.</param>
        /// <param name="relatedId">The related id.</param>
        public static void AddWorkItemLink(WorkItem wi, int relatedId)
        {
            RelatedLink link = new RelatedLink(relatedId);
            wi.Links.Add(link);
        }

        /// <summary>
        /// Adds the change set link.
        /// </summary>
        /// <param name="wi">The wi.</param>
        /// <param name="changeSetId">The change set id.</param>
        public void AddChangeSetLink(WorkItem wi, int changeSetId)
        {
            RegisteredLinkTypeCollection linkTypes = this.store.RegisteredLinkTypes;
            RegisteredLinkType linkTypeChangeSet = linkTypes["Changeset"];
            ExternalLink link = new ExternalLink(linkTypeChangeSet, changeSetId.ToString());
            wi.Links.Add(link);
        }

        /// <summary>
        /// Adds the attachments.
        /// </summary>
        /// <param name="wi">The wi.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="comment">The comment.</param>
        public void AddAttachments(WorkItem wi, string fileName, string comment = "")
        {
            // TFS will search for the file, so make sure it exists.
            Attachment a = new Attachment(fileName, comment);
            wi.Attachments.Add(a);
        }

        /// <summary>
        /// Creates the work item.
        /// </summary>
        /// <param name="workItemType">Type of the work item.</param>
        /// <param name="title">The title.</param>
        /// <param name="areaPath">The area path.</param>
        /// <param name="iterationPath">The iteration path.</param>
        /// <returns></returns>
        public WorkItem CreateWorkItem(WorkItemTypes workItemType, string title, string areaPath, string iterationPath)
        {
            WorkItemType wiType = GetWorkItemType(workItemType);

            // Define Work Item Type and Create new Work Item
            WorkItem workItem = new WorkItem(wiType);

            workItem.AreaPath = areaPath;
            workItem.IterationPath = iterationPath;
            workItem.Title = title;
            workItem.Fields["Assigned To"].Value = this.GetActualUserName();

            Validate(workItem);

            workItem.Save();

            return workItem;
        }

        /// <summary>
        /// Makes the flat list.
        /// </summary>
        /// <param name="nodeCollection">The node collection.</param>
        /// <returns></returns>
        private static List<Node> MakeFlatList(NodeCollection nodeCollection)
        {
            List<Node> nodes = new List<Node>();

            foreach (Node item in nodeCollection)
            {
                nodes.Add(item);

                if (item.HasChildNodes)
                {
                    nodes.AddRange(MakeFlatList(item.ChildNodes));
                }
            }

            return nodes;
        }

        /// <summary>
        /// Gets the type of the work item.
        /// </summary>
        /// <param name="workItemType">Type of the work item.</param>
        /// <returns></returns>
        private WorkItemType GetWorkItemType(WorkItemTypes workItemType)
        {
            WorkItemType wiType = null;
            switch (workItemType)
            {
                case WorkItemTypes.Bug:
                    wiType = this.WorkItemTypeCollection["Bug"];
                    break;
                case WorkItemTypes.Task:
                default:
                    wiType = this.WorkItemTypeCollection["Task"];
                    break;
            }
            return wiType;
        }

        /// <summary>
        /// Validates the specified work item.
        /// </summary>
        /// <param name="workItem">The work item.</param>
        private static void Validate(WorkItem workItem)
        {
            List<Field> invalidFields = new List<Field>();
            foreach (Field field in workItem.Fields)
            {
                if (!field.IsValid)
                {
                    invalidFields.Add(field);
                    Tracer.AddDatabaseWarning("Invalid Field '{0}': {1}", field.Name, field.Status.ToString());
                    Tracer.AddDatabaseWarning("Current Value: '{0}'", field.Value);
                }
            }

            //There are some Invalid Fields.
            if (invalidFields.Count > 0)
            {
                Tracer.AddDatabaseWarning("Invalid Bug Track ID:{0}", invalidFields.ToArray());
                return;
            }
            else
            {
                workItem.Save();
                Tracer.AddDatabaseInformation("Work Item #{0} Successfully saved", workItem.Id);
            }
        }

        private string GetActualUserName()
        {
            string displayName = string.Empty;

            using (DirectorySearcher searcher = new DirectorySearcher(new DirectoryEntry(string.Empty)))
            {
                // nach Kriterium filtern - hier nach Gruppe mit einem best. Namen (Inhalt von 'username')
                searcher.Filter = string.Concat(string.Format(@"(&(ObjectClass=user)(sAMAccountName={0}))", System.Environment.UserName.ToLowerInvariant()));

                // Anfrage mit gesetzteen Filter ausführen und Ergebnisse durch iterieren
                foreach (SearchResult result in searcher.FindAll())
                {
                    displayName = result.Properties["displayname"][0].ToString();
                    break;
                }
            }

            return displayName;
        }
    }
}